---
sidebar_label: Kubernetes
description: |-
  The Kubernetes auth method allows automated authentication of Kubernetes
  Service Accounts.
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Kubernetes auth method

:::warning

**Note**: This engine can use external X.509 certificates as part of TLS or signature validation.
   Verifying signatures against X.509 certificates that use SHA-1 is deprecated and is no longer
   usable without a workaround. See the
   [deprecation FAQ](/docs/deprecation/faq#q-what-is-the-impact-of-removing-support-for-x-509-certificates-with-signatures-that-use-sha-1)
   for more information.

:::

The `kubernetes` auth method can be used to authenticate with OpenBao using a
Kubernetes Service Account Token. This method of authentication makes it easy to
introduce an OpenBao token into a Kubernetes Pod.

You can also use a Kubernetes Service Account Token to [log in via JWT auth][k8s-jwt-auth].
See the section on [How to work with short-lived Kubernetes tokens][short-lived-tokens]
for a summary of why you might want to use JWT auth instead and how it compares to
Kubernetes auth.

:::info

**Note:** If you are upgrading to Kubernetes v1.21+, ensure the config option
`disable_iss_validation` is set to true. Assuming the default mount path, you
can check with `bao read -field disable_iss_validation auth/kubernetes/config`.
See [Kubernetes 1.21](#kubernetes-1-21) below for more details.

:::

## Authentication

### Via the CLI

The default path is `/kubernetes`. If this auth method was enabled at a
different path, specify `-path=/my-path` in the CLI.

```shell-session
$ bao write auth/kubernetes/login role=demo jwt=...
```

### Via the API

The default endpoint is `auth/kubernetes/login`. If this auth method was enabled
at a different path, use that value instead of `kubernetes`.

```shell-session
$ curl \
    --request POST \
    --data '{"jwt": "<your service account jwt>", "role": "demo"}' \
    http://127.0.0.1:8200/v1/auth/kubernetes/login
```

The response will contain a token at `auth.client_token`:

```json
{
  "auth": {
    "client_token": "38fe9691-e623-7238-f618-c94d4e7bc674",
    "accessor": "78e87a38-84ed-2692-538f-ca8b9f400ab3",
    "policies": ["default"],
    "metadata": {
      "role": "demo",
      "service_account_name": "myapp",
      "service_account_namespace": "default",
      "service_account_secret_name": "myapp-token-pd21c",
      "service_account_uid": "aa9aa8ff-98d0-11e7-9bb7-0800276d99bf"
    },
    "lease_duration": 2764800,
    "renewable": true
  }
}
```

## Configuration

Auth methods must be configured in advance before users or machines can
authenticate. These steps are usually completed by an operator or configuration
management tool.

1. Enable the Kubernetes auth method:

  ```bash
  bao auth enable kubernetes
  ```

1. Use the `/config` endpoint to configure OpenBao to talk to Kubernetes. Use
  `kubectl cluster-info` to validate the Kubernetes host address and TCP port.
  For the list of available configuration options, please see the
  [API documentation](/api-docs/auth/kubernetes).

  ```bash
  bao write auth/kubernetes/config \
      token_reviewer_jwt="<your reviewer service account JWT>" \
      kubernetes_host=https://192.168.99.100:<your TCP port or blank for 443> \
      kubernetes_ca_cert=@ca.crt
  ```

:::danger

 **Note:** The pattern OpenBao uses to authenticate Pods depends on sharing
  the JWT token over the network. Given the [security model of
  OpenBao](/docs/internals/security), this is allowable because OpenBao is
  part of the trusted compute base. In general, Kubernetes applications should
  **not** share this JWT with other applications, as it allows API calls to be
  made on behalf of the Pod and can result in unintended access being granted
  to 3rd parties.

:::

1. Create a named role:

  ```text
  bao write auth/kubernetes/role/demo \
      bound_service_account_names=myapp \
      bound_service_account_namespaces=default \
      policies=default \
      ttl=1h
  ```

  This role authorizes the "myapp" service account in the default
  namespace and it gives it the default policy.

  For the complete list of configuration options, please see the [API
  documentation](/api-docs/auth/kubernetes).

## Kubernetes 1.21

Starting in version [1.21][k8s-1.21-changelog], the Kubernetes
`BoundServiceAccountTokenVolume` feature defaults to enabled. This changes the
JWT token mounted into containers by default in two ways that are important for
Kubernetes auth:

* It has an expiry time and is bound to the lifetime of the pod and service account.
* The value of the JWT's `"iss"` claim depends on the cluster's configuration.

The changes to token lifetime are important when configuring the
[`token_reviewer_jwt`](/api-docs/auth/kubernetes#token_reviewer_jwt) option.
If a short-lived token is used,
Kubernetes will revoke it as soon as the pod or service account are deleted, or
if the expiry time passes, and OpenBao will no longer be able to use the
`TokenReview` API. See [How to work with short-lived Kubernetes tokens][short-lived-tokens]
below for details on handling this change.

In response to the issuer changes, Kubernetes auth has been updated to not
validate the issuer by default. The Kubernetes API does the same validation when
reviewing tokens, so enabling issuer validation on the OpenBao side is
duplicated work. Without disabling OpenBao's issuer validation, it is not
possible for a single Kubernetes auth configuration to work for default mounted
pod tokens with both Kubernetes 1.20 and 1.21.. See [Discovering the service
account `issuer`](#discovering-the-service-account-issuer) below for guidance if
you wish to enable issuer validation in OpenBao.

[k8s-1.21-changelog]: https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.21.md#api-change-2
[short-lived-tokens]: #how-to-work-with-short-lived-kubernetes-tokens

### How to work with short-lived kubernetes tokens

There are a few different ways to configure auth for Kubernetes pods when
default mounted pod tokens are short-lived, each with their own tradeoffs. This
table summarizes the options, each of which is explained in more detail below.

| Option                               | All tokens are short-lived | Can revoke tokens early | Other considerations                                      |
|--------------------------------------|----------------------------|-------------------------|-----------------------------------------------------------|
| Use local token as reviewer JWT      | Yes                        | Yes                     | Requires OpenBao to be deployed on the Kubernetes cluster |
| Use client JWT as reviewer JWT       | Yes                        | Yes                     | Operational overhead                                      |
| Use long-lived token as reviewer JWT | No                         | Yes                     |                                                           |
| Use JWT auth instead                 | Yes                        | No                      |                                                           |

:::info

**Note:** By default, Kubernetes currently extends the lifetime of admission
injected service account tokens to a year to help smooth the transition to
short-lived tokens. If you would like to disable this, set
[--service-account-extend-token-expiration=false][k8s-extended-tokens] for
`kube-apiserver` or specify your own `serviceAccountToken` volume mount. See
[here](/docs/auth/jwt/oidc-providers/kubernetes#specifying-ttl-and-audience) for an example.

:::

[k8s-extended-tokens]: https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/#options

#### Use local service account token as the reviewer JWT

When running OpenBao in a Kubernetes pod the recommended option is to use the pod's local
service account token. OpenBao will periodically re-read the file to support
short-lived tokens. To use the local token and CA certificate, omit
`token_reviewer_jwt` and `kubernetes_ca_cert` when configuring the auth method.
OpenBao will attempt to load them from `token` and `ca.crt` respectively inside
the default mount folder `/var/run/secrets/kubernetes.io/serviceaccount/`.

```bash
bao write auth/kubernetes/config \
    kubernetes_host=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT
```

#### Use the OpenBao client's JWT as the reviewer JWT

When configuring Kubernetes auth, you can omit the `token_reviewer_jwt`, and OpenBao
will use the OpenBao client's JWT as its own auth token when communicating with
the Kubernetes `TokenReview` API. If OpenBao is running in Kubernetes, you also need
to set `disable_local_ca_jwt=true`.

This means OpenBao does not store any JWTs and allows you to use short-lived tokens
everywhere but adds some operational overhead to maintain the cluster role
bindings on the set of service accounts you want to be able to authenticate with
OpenBao. Each client of OpenBao would need the `system:auth-delegator` ClusterRole:

```bash
kubectl create clusterrolebinding openbao-client-auth-delegator \
  --clusterrole=system:auth-delegator \
  --group=group1 \
  --serviceaccount=default:svcaccount1 \
  ...
```

#### Continue using long-lived tokens

You can create a long-lived secret using the instructions [here][k8s-create-secret]
and use that as the `token_reviewer_jwt`. In this example, the `openbao` service
account would need the `system:auth-delegator` ClusterRole:

```bash
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: openbao-k8s-auth-secret
  annotations:
    kubernetes.io/service-account.name: openbao
type: kubernetes.io/service-account-token
EOF
```

Using this maintains previous workflows but does not benefit from the improved
security posture of short-lived tokens.

[k8s-create-secret]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#manually-create-a-service-account-api-token

#### Use JWT auth

Kubernetes auth is specialized to use Kubernetes' `TokenReview` API. However, the
JWT tokens Kubernetes generates can also be verified using Kubernetes as an OIDC
provider. The JWT auth method documentation has [instructions][k8s-jwt-auth] for
setting up JWT auth with Kubernetes as the OIDC provider.

[k8s-jwt-auth]: /docs/auth/jwt/oidc-providers/kubernetes

This solution allows you to use short-lived tokens for all clients and removes
the need for a reviewer JWT. However, the client tokens cannot be revoked before
their TTL expires, so it is recommended to keep the TTL short with that
limitation in mind.

### Discovering the service account `issuer`

:::info

**Note:** `disable_iss_validation` and `issuer` are deprecated and the
default for `disable_iss_validation` has changed to `true` for new Kubernetes
auth mounts. The following section only applies if you have set
`disable_iss_validation=false` , but `disable_iss_validation=true` is the new
recommended value for all versions of OpenBao.

:::

Kubernetes 1.21+ clusters may require setting the service account
[`issuer`](/api-docs/auth/kubernetes#issuer) to the same value as
`kube-apiserver`'s `--service-account-issuer` flag. This is because the service
account JWTs for these clusters may have an issuer specific to the cluster
itself, instead of the old default of `kubernetes/serviceaccount`. If you are
unable to check this value directly, you can run the following and look for the
`"iss"` field to find the required value:

```bash
echo '{"apiVersion": "authentication.k8s.io/v1", "kind": "TokenRequest"}' \
  | kubectl create -f- --raw /api/v1/namespaces/default/serviceaccounts/default/token \
  | jq -r '.status.token' \
  | cut -d . -f2 \
  | base64 -d
```

Most clusters will also have that information available at the
`.well-known/openid-configuration` endpoint:

```bash
kubectl get --raw /.well-known/openid-configuration | jq -r .issuer
```

This value is then used when configuring Kubernetes auth, e.g.:

```bash
bao write auth/kubernetes/config \
  kubernetes_host="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT" \
  issuer="\"test-aks-cluster-dns-d6cbb78e.hcp.uksouth.azmk8s.io\""
```

## Configuring kubernetes

This auth method accesses the [Kubernetes TokenReview API][k8s-tokenreview] to
validate the provided JWT is still valid. Kubernetes should be running with
`--service-account-lookup`. This is defaulted to true from Kubernetes 1.7.
Otherwise deleted tokens in Kubernetes will not be properly revoked and
will be able to authenticate to this auth method.

Service Accounts used in this auth method will need to have access to the
TokenReview API. If Kubernetes is configured to use RBAC roles, the Service
Account should be granted permissions to access this API. The following
example ClusterRoleBinding could be used to grant these permissions:

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: role-tokenreview-binding
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
  - kind: ServiceAccount
    name: openbao-auth
    namespace: default
```

## API

The Kubernetes Auth Plugin has a full HTTP API. Please see the
[API docs](/api-docs/auth/kubernetes) for more details.

[k8s-tokenreview]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#tokenreview-v1-authentication-k8s-io

## Code example

The following example demonstrates the Kubernetes auth method to authenticate
with OpenBao.

<Tabs>
<TabItem value="Go" heading="Go">

```go
package main

import (
	"fmt"
	"os"

	openbao "github.com/openbao/openbao/api/v2"
	auth "github.com/openbao/openbao/api/auth/kubernetes/v2"
)

// Fetches a key-value secret (kv-v2) after authenticating to OpenBao with a Kubernetes service account.
// For a more in-depth setup explanation, please see the relevant readme in the hashicorp/vault-examples repo.
func getSecretWithKubernetesAuth() (string, error) {
	// If set, the VAULT_ADDR environment variable will be the address that
	// your pod uses to communicate with OpenBao.
	config := openbao.DefaultConfig() // modify for more granular configuration

	client, err := openbao.NewClient(config)
	if err != nil {
		return "", fmt.Errorf("unable to initialize OpenBao client: %w", err)
	}

	// The service-account token will be read from the path where the token's
	// Kubernetes Secret is mounted. By default, Kubernetes will mount it to
	// /var/run/secrets/kubernetes.io/serviceaccount/token, but an administrator
	// may have configured it to be mounted elsewhere.
	// In that case, we'll use the option WithServiceAccountTokenPath to look
	// for the token there.
	k8sAuth, err := auth.NewKubernetesAuth(
		"dev-role-k8s",
		auth.WithServiceAccountTokenPath("path/to/service-account-token"),
	)
	if err != nil {
		return "", fmt.Errorf("unable to initialize Kubernetes auth method: %w", err)
	}

	authInfo, err := client.Auth().Login(context.TODO(), k8sAuth)
	if err != nil {
		return "", fmt.Errorf("unable to log in with Kubernetes auth: %w", err)
	}
	if authInfo == nil {
		return "", fmt.Errorf("no auth info was returned after login")
	}

	// get secret from OpenBao, from the default mount path for KV v2 in dev mode, "secret"
	secret, err := client.KVv2("secret").Get(context.Background(), "creds")
	if err != nil {
		return "", fmt.Errorf("unable to read secret: %w", err)
	}

	// data map can contain more than one key-value pair,
	// in this case we're just grabbing one of them
	value, ok := secret.Data["password"].(string)
	if !ok {
		return "", fmt.Errorf("value type assertion failed: %T %#v", secret.Data["password"], secret.Data["password"])
	}

	return value, nil
}
```

</TabItem>
</Tabs>
